import java.util.ArrayList;
import java.util.StringTokenizer;


/**
 * 
 * Initiates the Paxos state machine and the roles
 *
 */

public class PaxosStateMachine {

	/**
	 * @param args
	 * args - noderole (c,l,a), pid:port, dropprob, remotehost, upSet  
	 */
	
	public static int decideRole(PaxosPeer peer)
	{
		// Decide peer role. Leader or Acceptor.
		int lowestPid = -1;
		
		for (int i = 0; i<peer.upSet.size() && lowestPid == -1;i++)
		{
			if (peer.upSet.get(i) != PaxosConsts.DEAD_PROCESS_PORT)
			{
				lowestPid = i;
			}
		}
		peer.currentLeader = lowestPid;
		
		if (peer.currentRole == PaxosConsts.PLAY_CLIENT)
		{
			
			return PaxosConsts.PLAY_CLIENT;
		}
		if (peer.pid == lowestPid)
		{
			System.out.println("PID: "+peer.pid+" - Role change invoked. I am the NEW LEADER: ");
			return PaxosConsts.PLAY_LEADER;
		}
		System.out.println("PID: "+peer.pid+" - Role change invoked. I am an ACCEPTOR now. The current leader is PID:"+peer.currentLeader);
		return PaxosConsts.PLAY_ACCEPTOR;
	}

	public static void main(String[] args) 
	{
		// Scan the first set of arguments to get information
		// about the current node
		
		//  init upset
		ArrayList<Integer> upSet = new ArrayList<Integer>();
		for (int i=0;i<PaxosConsts.UPSET_SIZE;i++)
		{
			upSet.add(i,PaxosConsts.DEAD_PROCESS_PORT);
		}
 		StringTokenizer portPid = new StringTokenizer(args[PaxosConsts.ARG_NODE_PID_PORT],":");
		int pid = Integer.parseInt(portPid.nextToken());
		int port = Integer.parseInt(portPid.nextToken());
		float dropProb = Float.parseFloat(args[PaxosConsts.ARG_NODE_DROP_PROB]);
		int currentRole = Integer.parseInt(args[PaxosConsts.ARG_NODE_ROLE]);
		int retriesBound = Integer.parseInt(args[PaxosConsts.ARG_NODE_RETRY]);
		String remoteHost = args[PaxosConsts.ARG_NODE_HOST];
		int timeoutBound = Integer.parseInt(args[PaxosConsts.ARG_NODE_TIMEOUT]);
		// Scan the remaining inputs to get information
		// about rest of the nodes of the system
		int opid=0;
		int oport =0;
		int numPeers = 0;
		for (int i=6;i < args.length; i++)
		{
	 		portPid = new StringTokenizer(args[i],":");
			opid = Integer.parseInt(portPid.nextToken());
			oport = Integer.parseInt(portPid.nextToken());
			upSet.set(opid, oport);
			numPeers++;
			
		}
		PaxosConsts.NUM_PEERS = numPeers -1;
		System.out.println("PID: "+pid+" - The number of peers in the system is:" + numPeers);
		
		// create new peer with the inputs
		PaxosPeer peer = new PaxosPeer(pid,port,dropProb,currentRole,remoteHost,upSet,timeoutBound,retriesBound);
		
		int currentProtocolState = PaxosConsts.INIT;
		// while current state of the system is not operations done, keep looping
		while (currentProtocolState != PaxosConsts.SESSION_DONE)
		{
			int newRole = decideRole(peer);
			switch(newRole)
			{
				case PaxosConsts.PLAY_LEADER:
					// invoke leader code
					currentProtocolState = peer.playLeader();
				break;
				
				case PaxosConsts.PLAY_ACCEPTOR:
					// invoke acceptor code
					currentProtocolState = peer.playAcceptor();
				break;
				
				case PaxosConsts.PLAY_CLIENT:
					// invoke acceptor code
					currentProtocolState = peer.playClient();
				break;			
			}
		}
	}
}
